package com.wcom.esp.utils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;


/**This class is used to serialize Java Objects into<br>
 * String form to be used in javascript with eval(str);  This wiill <br>
 * parse the string automatically into native javascript<br>
 * used in conjunction with XMLHTTPRequest you can do ajax with <br>
 * little effort. 
 * @author Justin Thomas
 *
 */
public class JSONSerializer {
	private int count = 0;
	
	public JSONSerializer() {}

	public String serialize(Object o,String []methods) {
		return serializeWithMethods(o,methods);
	}
	
	public String getCount() { return "\"count\":\"" + count + "\""; }
	public void resetCount() { count = 0; }
	
	public String serialize(Object[] arr) {
		StringBuffer json = new StringBuffer();
		json.append("[");
		for(int i = 0; i<arr.length;i++) {
			json.append(serialize(arr[i]));
			if(i<arr.length-1) json.append(",");
		}
		json.append("]");
		return json.toString();
	}
		
	public String serialize(Object o) {
		if(o instanceof Collection) return serialize((Collection)o);
		
		StringBuffer json = new StringBuffer();
		count++;
		getStart(o,json);
		serializeWithoutMethods(o,json);
		getEnd(json);
		return json.toString(); 
	}

	private String serializeWithMethods(Object o,String[] methods) {
		StringBuffer json = new StringBuffer();
		getStart(o,json);
		for(int i = 0;i<methods.length;i++) {
			String methodName = methods[i];
			String value = getValue(o,methodName).toString();
			writeJsonField(methodName,value,json);
			if(i < methods.length-1) {
				json.append(",");
			}
		}
		getEnd(json);
		return json.toString();
	}

	private void serializeWithoutMethods(Object o,StringBuffer json) {
		Class c;
		try {
			c = Class.forName(o.getClass().getName());
		} catch (ClassNotFoundException e1) {
			e1.printStackTrace();
			return;
		}
		Field[] fields = c.getDeclaredFields();
		
		for(int i = 0; i<fields.length;i++) {
			String name = fields[i].getName();
			Object value = null;
			String method;
			try {
				if(name.charAt(0)=='_') name = name.substring(1);
				
				//System.out.println(name);
				String first = ""+name.charAt(0);
				first = first.toUpperCase();
				method = "get" + first + name.substring(1);
				String methodIs = "is" + first + name.substring(1);
				
				value = getValue(o,method);
				
				if(value instanceof Collection) {
					writeJsonFieldList(name,serialize((Collection)value),json);	
				} else if(isPrimitive(value)) {
					writeJsonField(name,value,json);
				} else {
					writeJsonObject(name,serialize(value),json);
				}
		
			} catch(NullPointerException e) {
				writeJsonField(name,"",json);
			} catch(Exception e) {
				System.out.println("Class: " + o.getClass().getName() +"/Field: "+
						fields[i].getName() + " NOT Serialized.");
				continue;
			}

			
		}

	}
	
	public String serialize(Collection list) {
		StringBuffer b = new StringBuffer("[");
		for(Iterator i = list.iterator();i.hasNext();) {
			b.append(serialize(i.next()));
			if(i.hasNext()) b.append(",");
		}
		b.append("]");
		return b.toString();
	}
	
	private void writeJsonField(String name,Object value,StringBuffer json) {
		json.append(",\n");
		json.append("\"").append(name).append("\":\"")
		.append(value.toString()).append("\"");
	
	}
	
	private void writeJsonFieldList(String name,Object value,StringBuffer json) {
		json.append(",\n");
		json.append("\"").append(name).append("\":").append(value.toString());
	
	}

	private void writeJsonObject(String name,Object value,StringBuffer json) {
		json.append(",\n");
		json.append("\"")
		.append(name)
		.append("\":{")
		.append(value.toString())
		.append("}");
	
	}
	
	private Object getValue(Object o,String getMethod) {
		try {
			Method method = o.getClass().getMethod(getMethod, (Class[])null);
			Object value = method.invoke(o, (Object[])null);
			return value;
		} catch (Exception e) {
			//e.printStackTrace();
			throw new RuntimeException(e);
		} 
	}

	private void getStart(Object o,StringBuffer json) {
		String start = "{ \n";
		String className = getClassName(o);
		start += "\"class\": \"" + className + "\"";
		json.append(start);
	}

	private void getEnd(StringBuffer json) {
		json.append("}\n");
	}

	private String getClassName(Object o) {
		String packageClassName = o.getClass().getName();
		return packageClassName;
	}
	
	private boolean isPrimitive(Object o) {
		Class[] primitives = {Boolean.class,String.class,Float.class,Long.class,
				Integer.class,Byte.class,Short.class,Double.class};
		
		for(int i = 0; i<primitives.length;i++) {
			if(primitives[i].equals(o.getClass())) return true;
		}
		return false;
	}

}

/* syntax we are trying to achieve

{ 
"class": "Person", 
"name": "William Shakespeare", 
"birthday": -12802392000000, 
"nickname": "Bill" 
"phoneNumbers": [ { "class": "Phone", "name": "cell", "number": "555-123-4567" }, 
{ "class": "Phone", "name": "home", "number": "555-987-6543" }] }
 */ 
